package org.zjvis.datascience.common.util;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.zjvis.datascience.common.model.stat.Range;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.DateTimeException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;

/**
 * @description 时间处理工具类
 * @date 2021-09-18
 */
public class TimeUtil {

    public static final Integer YEAR = 1;
    public static final Integer SEASON = 2;
    public static final Integer MONTH = 3;
    public static final Integer WEEK = 4;
    public static final Integer DAY = 5;
    public static final Integer HOUR = 6;
    public static final Integer MINUTE = 7;
    public static final Integer SECOND = 8;

    public static final Long SPAN_FIFTEEN_MINUTE = 15 * 60 * 1000l;
    public static final Long SPAN_HOUR = 60 * 60 * 1000l;
    public static final Long HALF_DAY = 12 * 60 * 60 * 1000l;
    public static final Long SPAN_TWO_WEEK = 2 * 7 * 24 * 60 * 60 * 1000l;
    public static final Long SPAN_THREE_MONTH = 3 * 30 * 24 * 60 * 60 * 1000l;
    public static final Long SPAN_FIFTEEN_MONTH = 15 * 30 * 24 * 60 * 60 * 1000l;
    public static final Long SPAN_FIFTEEN_YEAR = 15 * 365 * 24 * 60 * 60 * 1000l;
    public static final Long SPAN_ONE_ERA = 3153600000000l;

    public static final String DATETIME_HOUR_PATTERN = "yyyy-MM-dd HH";
    public static final String SIMPLE_DATE_FORMAT = "yyyy-MM-dd";
    public static final String FULL_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";

    public static final int SECOND_ONE_DAY = 60 * 60 * 24;

    public static final int SECOND_ONE_MIN = 60;

    public static String format(String time, String from, String to) throws ParseException {
        if (StringUtils.isEmpty(from) || StringUtils.isEmpty(to) || from.equals(to)) {
            return time;
        }
        SimpleDateFormat fromFormat = new SimpleDateFormat(from);
        SimpleDateFormat toFormat = new SimpleDateFormat(to);
        Date date = fromFormat.parse(time);
        return toFormat.format(date);
    }

    //返回给定时刻到23:59:59的剩余时间，单位秒
    public static int retainSecond(Date date) {
        SimpleDateFormat dateFormat = new SimpleDateFormat(FULL_DATE_FORMAT);
        String nowStr = dateFormat.format(date);

        String endStr = nowStr.substring(0, SIMPLE_DATE_FORMAT.length()) + " 23:59:59";

        try {
            long retain = (dateFormat.parse(endStr).getTime() - dateFormat.parse(nowStr).getTime()) / 1000;
            return (int) retain;
        } catch (ParseException e) {
            return 0;
        }
    }

    public static String formatDate(long time) {
        Date date = new Date(time);
        return formatDate(date, FULL_DATE_FORMAT);
    }

    public static String formatDate(Date date) {
        return formatDate(date, FULL_DATE_FORMAT);
    }

    public static String formatDate(Date date, String format) {
        if (date == null) {
            return "";
        }
        try {
            LocalDateTime ldt = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
            DateTimeFormatter dtFormat = DateTimeFormatter.ofPattern(format);
            return ldt.format(dtFormat);
        } catch (DateTimeException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }

    //计算给定时间戳的结尾时间, 如2017-03-21,返回2017-03-21 23:59:59
    //日期以31号作为结尾，即使给定年月没有31号查询结果不影响
    public static String completeTimestamp(String timeStamp, String format) {
        if (StringUtils.isEmpty(timeStamp) || StringUtils.isEmpty(format)) {
            return timeStamp;
        }

        if ("yyyy".equals(format)) {
            int days = getDays(Integer.parseInt(timeStamp), 12);
            return timeStamp + "-12-" + days + " 23:59:59";
        } else if ("yyyy-MM".equals(format)) {
            String[] split = StringUtils.split(timeStamp, "-");
            int days = getDays(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
            return timeStamp + "-" + days + " 23:59:59";
        } else if ("yyyy-MM-dd".equals(format)) {
            return timeStamp + " 23:59:59";
        } else if ("yyyy-MM-dd HH".equals(format)) {
            return timeStamp + ":59:59";
        } else if ("yyyy-MM-dd HH:mm".equals(format)) {
            return timeStamp + ":59";
        } else {
            return timeStamp;
        }
    }

    public static int getDays(int year, int month) {
        int days = 0;
        boolean isLeapYear = false;
        if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
            isLeapYear = true;
        } else {
            isLeapYear = false;
        }
        switch (month) {
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                days = 31;
                break;
            case 2:
                if (isLeapYear) {
                    days = 29;
                } else {
                    days = 28;
                }
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                days = 30;
                break;
            default:
                break;
        }
        return days;
    }

    public static List<Range> divisionDate(Date left, Date right, int segmentNum) {
        long span = DateUtil.betweenMs(left, right);
        DateField timeUnit;
        int step = 1;
        Date start;
        if (span < SPAN_FIFTEEN_MINUTE) {
            //1分钟间隔
            timeUnit = DateField.MINUTE;
            String format = DateUtil.format(left, DatePattern.NORM_DATETIME_MINUTE_PATTERN);
            start = DateUtil.parse(format, DatePattern.NORM_DATETIME_MINUTE_PATTERN);
        } else if (span < SPAN_HOUR) {
            //5分钟间隔
            timeUnit = DateField.MINUTE;
            step = 5;
            int minute = DateUtil.minute(left);
            int startMinute = minute / 5 * 5;
            String format = DateUtil.format(left, DATETIME_HOUR_PATTERN);
            start = DateUtil.parse(format, DATETIME_HOUR_PATTERN);
            start = DateUtil.offsetMinute(start, startMinute);
        } else if (span < HALF_DAY) {
            //1小时间隔
            timeUnit = DateField.HOUR;
            String format = DateUtil.format(left, DATETIME_HOUR_PATTERN);
            start = DateUtil.parse(format, DATETIME_HOUR_PATTERN);
        } else if (span < SPAN_TWO_WEEK) {
            //1天间隔
            timeUnit = DateField.DAY_OF_YEAR;
            start = DateUtil.beginOfDay(left);
        } else if (span < SPAN_THREE_MONTH) {
            //1周间隔
            timeUnit = DateField.WEEK_OF_YEAR;
            start = DateUtil.beginOfWeek(left);
        } else if (span < SPAN_FIFTEEN_MONTH) {
            //1月间隔
            timeUnit = DateField.MONTH;
            start = DateUtil.beginOfMonth(left);
        } else if (span < SPAN_FIFTEEN_YEAR) {
            //1年间隔
            timeUnit = DateField.YEAR;
            start = DateUtil.beginOfYear(left);
        } else if (span < SPAN_ONE_ERA) {
            //10年间隔
            timeUnit = DateField.YEAR;
            step = 10;
            int year = DateUtil.year(left);
            int startYear = year / 10 * 10;
            start = DateUtil.parse(String.valueOf(startYear), "yyyy");
        } else {
            segmentNum = segmentNum < 1 ? 13 : segmentNum;
            int leftYear = DateUtil.year(left);
            int rightYear = DateUtil.year(right);
            List<Range> ranges = NumericUtil.divisionInteger(leftYear, rightYear, segmentNum);
            List<Range> segments = Lists.newArrayList();
            for (Range range : ranges) {
                Range<String> dateRange = Range
                        .closedOpen(DateUtil.parse(String.valueOf(range.lowerEndpoint()), "yyyy").toString(),
                                DateUtil.parse(String.valueOf(range.upperEndpoint()), "yyyy").toString());
                segments.add(dateRange);
            }
            return segments;
        }
        List<Range> segments = Lists.newArrayList();
        while (true) {
            Date offset = DateUtil.offset(start, timeUnit, step);
            Range segment = Range.closedOpen(TimeUtil.formatDate(start), TimeUtil.formatDate(offset));
            segments.add(segment);
            start = offset;
            if (offset.after(right)) break;
        }
        return segments;
    }

    public static Long convertDateToTimeStamp(String date) {
        return convertDateToTimeStamp(date, FULL_DATE_FORMAT);
    }

    public static Long convertDateToTimeStamp(String date, String pattern) {
        SimpleDateFormat format = new SimpleDateFormat(pattern);
        try {
            return format.parse(date).getTime();
        } catch (ParseException e) {
            return null;
        }
    }


}
